package com.yanghui.agent.agent.springboot.c.interceptor;

import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlUpdateStatement;
import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitorAdapter;
import com.alibaba.druid.util.JdbcConstants;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.google.common.collect.Lists;
import com.yanghui.agent.agent.springboot.c.entity.Entity;
import org.apache.commons.lang.StringUtils;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.mapping.*;
import org.apache.ibatis.session.Configuration;

import java.util.List;

public abstract class AbstractDynamicFieldStrategy extends MySqlASTVisitorAdapter implements IDynamicFieldStrategy {

    private static final ThreadLocal<Entity> DYNAMIC_FIELD = new ThreadLocal<>();

    @Override
    public void execute(MappedStatement mappedStatement,Object[] args){
        try{
            BoundSql boundSql = mappedStatement.getBoundSql(args[1]);
            String sql = boundSql.getSql();
            List<SQLStatement> sqlStatements = SQLUtils.parseStatements(sql, JdbcConstants.MYSQL);
            if (CollectionUtils.isEmpty(sqlStatements)) {
                return;
            }
            if(args[1] instanceof Entity){
                Entity entity = (Entity) args[1];
                update(entity,mappedStatement,args);
            }else {
                MapperMethod.ParamMap map = (MapperMethod.ParamMap)args[1];
                update((Entity) map.get("param1"),mappedStatement,args);
            }
        }finally {
            DYNAMIC_FIELD.remove();
        }
    }


    private void update(Entity entity,MappedStatement mappedStatement,Object[] args){
        if(entity.getDynamicFields().isEmpty()){
            return;
        }
        DYNAMIC_FIELD.set(entity);
        BoundSql boundSql = mappedStatement.getBoundSql(args[1]);
        String sql = boundSql.getSql();
        List<SQLStatement> sqlStatements = SQLUtils.parseStatements(sql,JdbcConstants.MYSQL);
        if (CollectionUtils.isEmpty(sqlStatements)) {
            return;
        }
        for(SQLStatement sqlStatement : sqlStatements) {
            sqlStatement.accept(this);
        }
        String newSql = SQLUtils.toSQLString(sqlStatements, JdbcConstants.MYSQL);
        List<ParameterMapping> newParametersMappings = Lists.newArrayList(boundSql.getParameterMappings());

//        SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
//        if(sqlCommandType == SqlCommandType.INSERT){
//            newParametersMappings.addAll(this.buildInsertParameterMapping(entity,mappedStatement.getConfiguration()));
//        }else if(sqlCommandType == SqlCommandType.UPDATE){
//            newParametersMappings.addAll(this.buildUpdateParameterMapping(entity,mappedStatement.getConfiguration()));
//        }else {
//            return;
//        }

        BoundSql newBoundSql = new BoundSql(mappedStatement.getConfiguration(), newSql,
                newParametersMappings, boundSql.getParameterObject());
        MappedStatement.Builder builder =
                new MappedStatement.Builder(mappedStatement.getConfiguration(), mappedStatement.getId(),
                        new WarpBoundSqlSqlSource(newBoundSql), mappedStatement.getSqlCommandType());
        builder.cache(mappedStatement.getCache())
                .databaseId(mappedStatement.getDatabaseId())
                .fetchSize(mappedStatement.getFetchSize())
                .flushCacheRequired(mappedStatement.isFlushCacheRequired())
                .keyColumn(StringUtils.join(mappedStatement.getKeyColumns(), ','))
                .keyGenerator(mappedStatement.getKeyGenerator())
                .keyProperty(StringUtils.join(mappedStatement.getKeyProperties(), ','))
                .lang(mappedStatement.getLang())
                .parameterMap(mappedStatement.getParameterMap())
                .resource(mappedStatement.getResource())
                .resultMaps(mappedStatement.getResultMaps())
                .resultOrdered(mappedStatement.isResultOrdered())
                .resultSets(StringUtils.join(mappedStatement.getResultSets(), ','))
                .resultSetType(mappedStatement.getResultSetType())
                .statementType(mappedStatement.getStatementType())
                .timeout(mappedStatement.getTimeout()).useCache(mappedStatement.isUseCache());
        MappedStatement newMappedStatement = builder.build();
        args[0] = newMappedStatement;
    }



//    protected abstract List<ParameterMapping> buildInsertParameterMapping(Entity entity, Configuration configuration);
//
//    protected abstract List<ParameterMapping> buildUpdateParameterMapping(Entity entity,Configuration configuration);


    @Override
    public boolean visit(MySqlUpdateStatement x) {
        try{
            this.visit(x,DYNAMIC_FIELD.get());
        }catch (Exception e){
            e.printStackTrace();
        }
        return super.visit(x);
    }


    @Override
    public boolean visit(MySqlInsertStatement x) {
        try{
            this.visit(x,DYNAMIC_FIELD.get());
        }catch (Exception e){
            e.printStackTrace();
        }
        return super.visit(x);
    }

    protected abstract void visit(MySqlInsertStatement x, Entity entity);

    protected abstract void visit(MySqlUpdateStatement x, Entity entity);

    static class WarpBoundSqlSqlSource implements SqlSource {
        private final BoundSql boundSql;
        public WarpBoundSqlSqlSource(BoundSql boundSql) {
            this.boundSql = boundSql;
        }
        @Override
        public BoundSql getBoundSql(Object parameterObject) {
            return boundSql;
        }
    }
}
